package com.wencai.util;

import com.wencai.bean.LimitUpInfo;
import com.wencai.bean.PriceAction;
import com.wencai.bean.StockData;
import com.alibaba.excel.util.CollectionUtils;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 从文件中查询某只股票某天的价格
 *
 * @author wangpan
 * @date 2023/5/10
 */
public class PriceFinder {
    private static String FIVE_MINUTES_DIR = "D:\\Users\\Administrator\\Desktop\\记录\\5分线数据\\";
    private static String DAILY_DIR = "D:\\Users\\Administrator\\Desktop\\记录\\日线数据\\";

    private static Map<String, LinkedHashMap<String, StockData>> fiveMinutesCache = new HashMap<>();
    private static Map<String, StockData> dailyCache = new HashMap<>();

    public static double getUpLimitPrice(double preCLose) {
        return BigDecimal.valueOf(preCLose).multiply(BigDecimal.valueOf(1.1)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static double getLowLimitPrice(double preCLose) {
        return BigDecimal.valueOf(preCLose).multiply(BigDecimal.valueOf(0.9)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static String getCurrentDay() {
        return LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd"));
    }

    public static String findCurrentPrice(String stockName, String date, String timePoint, PriceAction priceAction, BuyStrategy buyStrategy) throws IOException {
        String index = date + timePoint + "00000";
        LinkedHashMap<String, StockData> map = getFiveMinutesData(stockName, date);
        if (map == null) {
            return null;
        }
        StockData dailyStockData = getDailyStockData(stockName, date);
        // 需要根据不同的SellStrategy来决定价格
        return StrategyExecutor.getBuyPrice(map, index, buyStrategy, priceAction, dailyStockData);
    }

    public static String findCurrentPriceWithData(LinkedHashMap<String, StockData> map, StockData dailyStockData, String date, String timePoint, PriceAction priceAction, BuyStrategy buyStrategy) {
        if (map == null) {
            return null;
        }
        String index = date.replace("-", "") + timePoint + "00000";
        return StrategyExecutor.getBuyPrice(map, index, buyStrategy, priceAction, dailyStockData);
    }

    public static String findNextPriceWithData(LinkedHashMap<String, StockData> map, StockData dailyStockData, String date, String timePoint, PriceAction priceAction, SellStrategy sellStrategy) {
        if (map == null) {
            return null;
        }
        String index = date + timePoint + "00000";
        return StrategyExecutor.getSellPrice(map, index, sellStrategy, priceAction, dailyStockData);
    }

    public static LimitUpInfo findLimitUpInfo(String stockName, String date) throws IOException {
        LimitUpInfo limitUpInfo = new LimitUpInfo();
        StockData dailyStockData = getDailyStockData(stockName, date);
        if (dailyStockData == null) {
            return limitUpInfo;
        }
        double preClose = dailyStockData.getPreClose();
        final double upLimitPrice = PriceFinder.getUpLimitPrice(preClose);
        double high = dailyStockData.getHigh();
        boolean isLimitUp = high == upLimitPrice;
        limitUpInfo.setIsLimitUp(isLimitUp ? "是" : "否");
        LinkedHashMap<String, StockData> fiveMinutesData = getFiveMinutesData(stockName, date);
        List<StockData> values = new ArrayList(fiveMinutesData.values());
        List<StockData> collect = values.stream().filter(item -> item.getLow() == upLimitPrice).collect(Collectors.toList());
        if (isLimitUp) {
            if (collect.isEmpty()) {
                limitUpInfo.setIsOpen("开板");
                return limitUpInfo;
            }
            limitUpInfo.setFirstLimitUpTime(getLastFiveMinutesTime(collect.get(0).getDate()));
            if (collect.size() == 1) {
                limitUpInfo.setIsOpen("开板或尾盘封板");
            } else {
                String firstDateTime = collect.get(0).getDate();
                int keyIndex = MapUtils.getKeyIndex(fiveMinutesData, firstDateTime);
                LinkedHashMap<String, StockData> map = MapUtils.subAfterMap(fiveMinutesData, keyIndex, false);
                double asDouble = map.values().stream().mapToDouble(StockData::getLow).min().getAsDouble();
                if (asDouble == upLimitPrice) {
                    limitUpInfo.setIsOpen("首封后未开板");
                } else {
                    limitUpInfo.setIsOpen("首封后开板");
                    // 最后开板时间
                    List<StockData> collect1 = map.values().stream().filter(item -> item.getLow() < upLimitPrice).collect(Collectors.toList());
                    StockData stockData = collect1.get(collect1.size() - 1);
                    limitUpInfo.setLastUnLimitUpTime(getTime(stockData.getDate()));
                }
            }
        }
        return limitUpInfo;
    }

    private static String getTime(String dateTime) {
        return dateTime.substring(8, 12);
    }

    private static String getLastFiveMinutesTime(String dateTime) {
        String time = getTime(dateTime);
        LocalTime localTime = LocalTime.of(Integer.valueOf(time.substring(0, 2)), Integer.valueOf(time.substring(2, 4)));
        return DateTimeFormatter.ofPattern("HHmm").format(localTime.minusMinutes(5));
    }

    public static String findNextPrice(String stockName, String date, String timePoint, PriceAction priceAction, SellStrategy sellStrategy) throws IOException {
        String nextDate = ExchangeDay.getNextExchangeDay(date);
        String index = nextDate + timePoint + "00000";
        LinkedHashMap<String, StockData> map = getFiveMinutesData(stockName, nextDate);
        if (map == null) {
            return null;
        }
        StockData dailyStockData = getDailyStockData(stockName, nextDate);
        // 需要根据不同的SellStrategy来决定价格
        return StrategyExecutor.getSellPrice(map, index, sellStrategy, priceAction, dailyStockData);
    }

    @Nullable
    public static LinkedHashMap<String, StockData> getFiveMinutesData(String stockName, String date) throws IOException {
        if (date.compareTo(getCurrentDay()) > 0) {
            return null;
        }
        String key = stockName + date;
        if (fiveMinutesCache.containsKey(key)) {
            return fiveMinutesCache.get(key);
        }
        String fileName = stockName + "-" + DateConverter.convertDate(date) + ".txt";
        List<String> list = IOUtils.readFromFile(FIVE_MINUTES_DIR + fileName);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        list.remove(0);
        LinkedHashMap<String, StockData> map = new LinkedHashMap<>();
        for (String str : list) {
            String[] split = str.split(",");
            String dateTime = split[1];
            String open = split[PriceAction.FIVE_MINUTES_OPEN.getValue()];
            String high = split[PriceAction.FIVE_MINUTES_HIGH.getValue()];
            String low = split[PriceAction.FIVE_MINUTES_LOW.getValue()];
            String close = split[PriceAction.FIVE_MINUTES_CLOSE.getValue()];
            map.put(split[1], new StockData(dateTime, Double.valueOf(open), Double.valueOf(high), Double.valueOf(low), Double.valueOf(close)));
        }
        fiveMinutesCache.put(key, map);
        return map;
    }

    @Nullable
    private static StockData getDailyStockData(String stockName, String date) throws IOException {
        if (date.compareTo(getCurrentDay()) > 0) {
            return null;
        }
        String key = stockName + date;
        if (dailyCache.containsKey(key)) {
            return dailyCache.get(key);
        }
        String fileName = stockName + "-" + DateConverter.convertDate(date) + ".txt";
        List<String> list = IOUtils.readFromFile(DAILY_DIR + fileName);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        list.remove(0);
        if (list.size() == 0) {
            return null;
        }
        String[] split = list.get(0).split(",");
        String dateTime = split[1];
        String open = split[PriceAction.DAILY_OPEN.getValue()];
        String high = split[PriceAction.DAILY_HIGH.getValue()];
        String low = split[PriceAction.DAILY_LOW.getValue()];
        String close = split[PriceAction.DAILY_CLOSE.getValue()];
        String preClose = split[PriceAction.DAILY_PRE_CLOSE.getValue()];

        StockData stockData = new StockData(dateTime, Double.valueOf(open), Double.valueOf(high), Double.valueOf(low), Double.valueOf(close));
        stockData.setPreClose(Double.valueOf(preClose));

        dailyCache.put(key, stockData);
        return stockData;
    }
}
